package btp.Calendars;

public class GregorianCalendar {

        public static final int JANUARY = 1;
        public static final int FEBRUARY = 2;
        public static final int MARCH = 3;
        public static final int APRIL = 4;
        public static final int MAY = 5;
        public static final int JUNE = 6;
        public static final int JULY = 7;
        public static final int AUGUST = 8;
        public static final int SEPTEMBER = 9;
        public static final int OCTOBER = 10;
        public static final int NOVEMBER = 11;
        public static final int DECEMBER = 12;
        
        public static final int EPOCH = 1;
        
        protected int year;
        protected int month;
        protected int day;
        protected boolean leap;
        
        public GregorianCalendar(int year,int month,int day)
        {
                setYear(year);
                setMonth(month);
                setDay(day);
        }
        
        protected void setLeap(){
                leap = isYearLeap(year);
        }
        
        public boolean isLeap(){
                return leap;
        }
        
        public int getYear() {
                return year;
        }
        public void setYear(int year) {
                this.year = year;
                setLeap();
        }
        public int getMonth() {
                return month;
        }
        public void setMonth(int month) {
                this.month = month;
        }
        public int getDay() {
                return day;
        }
        public void setDay(int day) {
                this.day = day;
        }
        
        
        public long getRDFromGregorian(){
                return getRDFromGregorian(year, month, day);
        }
        
        public int getOrdinalDay(){
                return (int) greagorianDateDifference(new GregorianCalendar(year-1, DECEMBER, 31),this);
        }
        
        public static boolean isYearLeap(int year){
                return (year%400==0)||(year%4==0 && year%100!=0);
        }
        
        
        public static long getRDFromGregorian(int year,int month,int day)
        {
                return (long) (EPOCH-1 + 365*(year-1) + Math.floor((year-1)/4.0) - Math.floor((year-1)/100.0) + Math.floor((year-1)/400.0) + Math.floor((367*month-362)/12.0) + ((month<=2)?0:(isYearLeap(year)?-1:-2))+day);
        }

        public static GregorianCalendar getDayFromRD(long rd){
                int year = getYearFromRD(rd);
                int prior_days = (int) (rd - getRDFromGregorian(year, JANUARY, 1));
                int correction = (rd<getRDFromGregorian(year, MARCH, 1))?0:(isYearLeap(year)?1:2);
                int month = (int) Math.floor((12*(prior_days+correction)+373)/367.0);
                int day = (int) (rd+1-getRDFromGregorian(year, month, 1));
                return new GregorianCalendar(year, month, day);
        }

		public static int getYearFromRD(long rd) {
			long d0 = (long) (rd - EPOCH);
			long n400 = (long) Math.floor(d0/146097.0);
			long d1 = d0%146097;
			d1=(d1<0)?d1+146097:d1;
			long n100 = (long) Math.floor(d1/36524);
			long d2 = d1%36524;
			d2=d2<0?d2+36524:d2;
			long n4 = (long) Math.floor(d2/1461.0); 
			long d3 = d2%1461;
			d3=d3<0?d3+1461:d3;
			long n1 = (long) Math.floor(d3/365.0);
			int year = (int) (400*n400 + 100*n100 + 4*n4 + n1); 
			year=(n100==4 || n1==4)? year:year+1;
			return year;
		}
        
        public static long greagorianDateDifference(GregorianCalendar g1, GregorianCalendar g2){
                return g2.getRDFromGregorian()-g1.getRDFromGregorian();
        }
        
        public GregorianCalendar nthDay(int n, int day){
                return getDayFromRD(RataDie.nthDay(n, day, getRDFromGregorian()));
        }
        
        public static String getStringForMonth(int month){
                switch(month){
                case JANUARY: return "January";
                case FEBRUARY: return "February";
                case MARCH: return "March";
                case APRIL: return "April";
                case MAY: return "May";
                case JUNE: return "June";
                case JULY: return "July";
                case AUGUST: return "August";
                case SEPTEMBER: return "September";
                case OCTOBER: return "October";
                case NOVEMBER: return "November";
                case DECEMBER: return "December";
                }
                return null;
        }

        public static void main(String[] args){
        	GregorianCalendar g = new GregorianCalendar(2012, 12, 21);
        	System.out.println(g.getRDFromGregorian()-GregorianCalendar.getRDFromGregorian(-3101, 1, 23));
        }
        
}
